#pragma once
#include "Sock.hpp"
#include "Err.hpp"
#include "Log.hpp"
#include<iostream>
#include <string>
#include <functional>
#include <cstring>
#include <cstdlib>
#include <sys/select.h>

const static int N = sizeof(fd_set) * 8; // select可以管理文件描述符为fd_set类型比特位
const static uint16_t defaultport = 8888;
using type_t =int;
class SelectServer
{
public:
    SelectServer(int port = defaultport)
        : port_(defaultport)
    {
    }
    void InitServer()
    {
        listensockfd_.Socket();
        listensockfd_.Bind(port_);
        listensockfd_.Listen();

        for (int i = 0; i < N; ++i)
        {
            fd_arry[i] = defaultfd;
        }

        fd_arry[0] = listensockfd_.Fd(); // 默认0号下标存储的是listen套接字
    }
    void Start()
    {
        // 1. 这里我们能够直接获取新的链接吗？
        // 2. 最开始的时候，我们的服务器是没有太多的sock的，甚至只有一个sock！listensock
        // 3. 在网络中， 新连接到来被当做 读事件就绪！
        // listensock_.Accept(); 不能！
        while (true)
        {
            // 1.设置我们需要关心的文件描述符，用户告诉内核
            fd_set rfds;
            FD_ZERO(&rfds);
            int maxsockfd = listensockfd_.Fd();
            for (int i = 0; i < N; ++i)
            {
                if (fd_arry[i] == defaultfd)
                    continue;
                FD_SET(fd_arry[i], &rfds);
                if (fd_arry[i] > maxsockfd)
                    maxsockfd = fd_arry[i];
            }
            // 2. 调用select接口
            // struct timeval timeout={0,0};
            // int n = select(maxsockfd + 1, &rfds, nullptr, nullptr, &timeout);
            int n = select(maxsockfd + 1, &rfds, nullptr, nullptr, nullptr);
            if (n > 0)
            {
                // 有事件就绪了，开始处理
                logMessage(Info, "有事件就绪了，开始处理相关事件");
                HandlerEvent(rfds);
            }
            else if (n == 0)
            {
                logMessage(Info, "select timeout ...");
            }
            else
            {
                logMessage(Warning, "select error:%s [code:%d]", strerror(errno), errno);
            }
        }
    }
    void HandlerEvent(fd_set &rfds)
    {
        for (int i = 0; i < N; ++i)
        {
            int fd = fd_arry[i];
            if (fd == defaultfd)
                continue;
            if ((FD_ISSET(fd, &rfds)) && (fd == listensockfd_.Fd())) // listen套接字有读事件发生了
            {
                Accepter();
            }
            if ((FD_ISSET(fd, &rfds)) && (fd != listensockfd_.Fd())) // 其它的套接字有读事件发生了
            {
                ServerIO(i);
            }
        }
    }
    void Accepter()
    {
        // 1.接受客户
        std::string clientip;
        uint16_t clientport;
        int sockfd = listensockfd_.Accept(&clientip, &clientport);
        if (sockfd < 0)
            return;
        // 2. 存储sockfd到fd_array数组中
        int index = 0;
        while (index < N)
        {
            if (fd_arry[index] == defaultfd)
                break;
            ++index;
        }
        if (index < N)
        {
            fd_arry[index] = sockfd;
            logMessage(Info, "存储%d套接字成功！", sockfd);
        }
        else
        {
            close(sockfd);
            logMessage(Warning, "存储%d失败,fd_arry[] is full", sockfd);
        }
    }
    void ServerIO(int i)
    {
        int fd=fd_arry[i];
        char buffer[1024];
        int n=recv(fd,buffer,sizeof(buffer)-1,0);
        if(n>0)
        {
            buffer[n]=0;
            std::cout<<"client#"<<buffer<<std::endl;

            std::string echo=buffer;
            echo+="[server echo client]";
            send(fd,echo.c_str(),echo.size(),0);//TODO
        }
        else
        {
            logMessage(Info,"clinet:%d quit...",fd);
            close(fd);
            fd_arry[i]=defaultfd;
        }
    }
    ~SelectServer() 
    {
        listensockfd_.Close();
    }

private:
    uint16_t port_;
    Sock listensockfd_;
    type_t fd_arry[N]; // 存储socket套接字
};